package zdream.pmfield.storage;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.io.InputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import zdream.pmfield.data.foe.FoeTemplate;
import zdream.pmfield.data.foe.Type;

/**
 * @author Zdream
 * @date 2023-11-09
 * @since 0.0.1
 */
public class FoesJsonLoader extends JsonLoader {
	public static List<FoeTemplate> loadFromPath(Path path) {
		final JsonArray array = fromPath(path).getAsJsonArray();
		return loadFromJsonArray(array);
	}

	public static List<FoeTemplate> loadFromInputStream(InputStream stream) {
		final JsonArray array = fromInputStream(stream).getAsJsonArray();
		return loadFromJsonArray(array);
	}

	public static List<FoeTemplate> loadFromJsonArray(JsonArray array) {
		return array.asList().stream()
				.map(JsonElement::getAsJsonObject)
				.map(FoesJsonLoader::loadOne)
				.filter(Objects::nonNull)
				.collect(Collectors.toList());
	}

	public static FoeTemplate loadOne(JsonObject object) {
		final int id = object.get("id").getAsInt();
		final int subId = Optional.ofNullable(object.get("subId")).map(JsonElement::getAsInt).orElse(0);
		FoeTemplate template = new FoeTemplate(id, subId);

		template.name = object.get("name").getAsString();

		JsonElement element = object.get("type");
		if (element == null) {
			System.err.printf("Foe Json #%d(%d): Invalid type%n", id, subId);
			return null;
		}
		if (element.isJsonArray()) {
			template.types = element.getAsJsonArray().asList().stream()
					.map(JsonElement::getAsString)
					.map(Type::valueOf)
					.toArray(Type[]::new);
		} else if (element.isJsonPrimitive()) {
			final char[] typeString = element.getAsString().toCharArray();
			template.types = new Type[typeString.length];
			for (int i = 0; i < typeString.length; i++) {
				template.types[i] = parseType(typeString[i]);
				if (template.types[i] == null) {
					System.err.printf("Foe Json #%d(%d): Invalid type%n", id, subId);
					return null;
				}
			}
		}

		element = object.get("bStat");
		if (element == null || !element.isJsonArray()) {
			System.err.printf("Foe Json #%d(%d): Invalid bStat%n", id, subId);
			return null;
		}
		JsonArray array = element.getAsJsonArray();
		for (int i = 0; i < template.bStats.length; i++) {
			template.bStats[i] = array.get(i).getAsShort();
		}

		element = object.get("vStat");
		if (element == null) {
			Arrays.fill(template.vStats, FoeTemplate.V_STAT_BASIC_VALUE);
		} else if (!element.isJsonArray()) {
			System.err.printf("Foe Json #%d(%d): Invalid bStat%n", id, subId);
			return null;
		} else {
			array = element.getAsJsonArray();
			for (int i = 0; i < template.vStats.length; i++) {
				template.vStats[i] = array.get(i).getAsShort();
			}
		}

		return template;
	}

	private static Type parseType(char ch) {
		switch (ch) {
		case 'N': return Type.NORMAL;
		case 'G': return Type.GRASS;
		case 'F': return Type.FIRE;
		case 'W': return Type.WATER;
		case '~': return Type.WIND;
		case 'S': return Type.SAND;
		case 'T': return Type.STEEL;
		case 'M': return Type.MIND;
		case 'E': return Type.ELECTRIC;
		case 'I': return Type.ICE;
		case 'D': return Type.DARK;
		case 'L': return Type.LIGHT;
		}
		return null;
	}
}
